/***************************************************************
* gfsServices : RNS services for metadata management
*
* @author : Nico A - 0606101793
* @version : 1.0
*
***************************************************************/

#include <stdio.h>
#include <string.h>
#include <mysql.h>
#include <stdlib.h>

//socket/net header
#include <sys/time.h>
#include <sys/types.h>
#include <errno.h>
#include <fcntl.h>
#include <netdb.h>

//icmp header
#include <netinet/ip_icmp.h>
#include <sys/param.h>

#include "gfsGlobal.h"
#include "gfsServices.h"

//GFS Definition
#define QUERY_LENGTH			1024
#define PATH_LENGTH				512
#define FILENAME_LENGTH			256
#define CONF_FILENAME			"gfs.conf"
#define GFS_PORT				"2811"
#define GFS_CHECK_HOST_TIMEOUT	2

//ICMP Definition
#define	DEFDATALEN	(64-ICMP_MINLEN)	/* default data length */
#define	MAXIPLEN	60
#define	MAXICMPLEN	76
#define	MAXPACKET	(65536 - 60 - ICMP_MINLEN)/* max packet size */


/******************************
* EXTERN FUNCTION DECLARATION *
*******************************/
//METADATA 
int		gfs_init_metadata();
int		gfs_open_metadata();
//SERVER METADATA 
int		gfs_get_available_server(GFS_server *server); //by refference
//FILE METADATA 
int		gfs_getFileLocation(GFS_file file); //by refference
int		gfs_saveFileMetadata(GFS_file file); //by refference
int		gfs_moveFileMetadata(GFS_file src_file, GFS_file dst_file); //by refference
int		gfs_getFileLocationMove(GFS_file src_file, GFS_file dst_file); //by refference
//DIRECTORY METADATA 
int		gfs_saveDirMetadata(GFS_dir dir); //by refference
int		gfs_moveDirMetadata(GFS_dir src_dir, GFS_dir dst_dir);
int		gfs_isDirExist(GFS_dir dir);
int		gfs_getItemList(GFS_dir dir, GFS_item *item); //by refference
//LOCK
int		gfs_lockMetadata(char *lock_mode, char *access_mode, int book, char *item_name, char *logical_loc);
int		gfs_unlockMetadata(int pid, int unbook, char *item_name, char *logical_loc);
//PERMISSION
int		gfs_get_permission_location(char *logical_path, char *mode);
int		gfs_get_permission(char *item_name, char *logical_location, char *mode);
int		gfs_change_permission(char *item_name, char *logical_location, char *mode);

/*******************************
* PRIVATE FUNCTION DECLARATION *
********************************/
void	gfs_close_metadata();
int		gfs_is_file_exist(char *filename, char *logical_location);
int		gfs_isDirExist_p(char *, char *);
int		gfs_read_conf_file(char *);
void	gfs_get_conf_file_section(char *conf_file_name, char *section, char *value);
void	gfs_permission_bin2str(char *bin, char *str);
void	gfs_get_physical_filename(char *filename, char *logical_location, char *physical_filename);
void	gfs_get_servername(char *physical_path, char *servername);
int		gfs_get_servername_and_size_from_logical_location(char *, char *, char *);
int		gfs_get_location_and_dirname(char *, char*, char *);
void	gfs_format_logical_location(char *, char *);
int		gfs_ping(char *);
void	*gfs_check_port(void *threadarg);


/**************************
* VARIABLES DECLARATION   *
***************************/

MYSQL			*conn;
MYSQL_RES		*res;
MYSQL_ROW		row;
MYSQL_FIELD		*field;

char server[FILENAME_LENGTH];
char user[FILENAME_LENGTH];
char password[FILENAME_LENGTH];
char database[FILENAME_LENGTH];

int i;
int j;
int rc;
int num_fields;
int num_rows;

char query[QUERY_LENGTH];

//gfs ping thread
int success_mirror=0;
pthread_mutex_t mutex1 = PTHREAD_MUTEX_INITIALIZER;


/*******************************
* END OF VARIABLES DECLARATION *
********************************/


/****************************************************************************
* lock
* lock statuses in database are : NULL for free, 's' for shared lock and 'x' for exclusive lock
* 'xs', 'xd' is for directory
* 
* @param:	lock_mode: x (exclusive lock), s (shared lock), xd, xs
*			access_mode: w (write access), r (read access) AND d (directory), f (file). 
*				Put it like this: "wd" to gain write access of directory
*			book: 1 yes, 0 no
*			item_name
* @return: GFS_SUCCESS if lock successful, negative value (error) if failed
****************************************************************************/
int gfs_lockMetadata(char *lock_mode, char *access_mode, int book, char *item_name, char *logical_loc)
{
	char lock[3];
	char permission_mode[2];
	int pid;
	
	//Open metadata database connection---------------------------------------
	if (gfs_open_metadata())
		verbose("S:gfs_lockMetadata(): Metadata connection success\n");
	else
	{
		verbose("S:gfs_lockMetadata(): ERROR: Metadata connection failed\n");
		return ECONN;
	}
	
	//STEP 1: Check whether logical_path already follows proper naming of logical_location 
	//(needs '/' at the end of the full directory path)
	char formatted_logical_location[strlen(logical_loc)+1];
	gfs_format_logical_location(formatted_logical_location, logical_loc);
	
	//STEP 2: Separate between logical directory name in which the file is located and
	//           the location of that directory (confused? lol) 
	char dir_logical_location[PATH_LENGTH] = "" ;
	char dir_name[FILENAME_LENGTH] = "";
	gfs_get_location_and_dirname(logical_loc, dir_logical_location, dir_name);
	
	//STEP 3: 
	//Check if book mode is 1(create new  locked item) --------------------------------------------------
	if (book==1)
	{
		char query_item_mode[2];

		//STEP 3.1.1: Check whether logical directory is exists or not--------
		if (gfs_isDirExist_p(dir_name, dir_logical_location)!=GFS_SUCCESS)
		{
			verbose("S:gfs_lockMetadata(): ERROR: Directory %s%s do not exists. failed put.\n", dir_logical_location, dir_name);
			return EDNOTVALID;	
		}
		
		//STEP 3.1.2: Check permission ---------------------------PERMISSION---------------------------------
		if (access_mode!=NULL)
		{
			sprintf(permission_mode, "%c", access_mode[0]);
			if (access_mode[1]=='d' && gfs_get_permission_location(formatted_logical_location, permission_mode)!=GFS_SUCCESS)
			{
				verbose("S:gfs_lockMetadata(): ERROR: You dont't have write permission on %s.\n", formatted_logical_location);
				return EPERMIT;
			}
		}		
						
		//STEP 3.1.3: check whether file with same name already exists-------------
		if (gfs_is_file_exist(item_name, formatted_logical_location)==GFS_SUCCESS)
		{
			verbose("S:gfs_lockMetadata(): ERROR: File %s%s already exists. Failed put.\n", formatted_logical_location, item_name);
			return EFEXIST;			
		}
		strcpy(query_item_mode, "f"); //harapdicek
	
		//STEP 3.1.4: Check whether directory with same name is already--------
		if (gfs_isDirExist_p(item_name, formatted_logical_location)==GFS_SUCCESS)
		{
			verbose("S:gfs_lockMetadata(): ERROR: Directory %s%s exists. failed put.\n", formatted_logical_location, item_name);
			return EDEXIST;	
		}
		strcpy(query_item_mode, "d");
		
		//start a sql transaction
		strcpy(query, "START TRANSACTION");
		if (mysql_query(conn, query))
		{
			fprintf(stderr, "%s\n", mysql_error(conn));
			return ECONN;
		}
				
		//STEP 3.1.5: Create new PID in database and retrieve it
		sprintf(query, "INSERT INTO pid (pid, idmetadata) VALUES (NULL, NULL);");
		if (mysql_query(conn, query))
		{
			fprintf(stderr, "%s\n", mysql_error(conn));
			gfs_rollback_metadata();
			return ECONN;
		}
		else 
		{
			num_rows = mysql_affected_rows(conn);

			if (num_rows == 0)
			{
				verbose("S:gfs_lockMetadata(): ERROR: Failed creating new PID in database!");
				gfs_rollback_metadata();
				return ERNS;
			}
			else 
				pid=mysql_insert_id(conn);
		}
				
		//STEP 3.1.6: All is well, we book a space in the database, and return the PID----------------------------------BOOK-----------------------------------------
		sprintf(query, "INSERT INTO metadata (filename, logical_location, created_date, type, locking, lockid) VALUES ('%s', '%s', NULL, '%s', 'x', LAST_INSERT_ID())"
			, item_name, formatted_logical_location, query_item_mode );
		//verbose("%s\n",query);
		if (mysql_query(conn, query))
		{
			fprintf(stderr, "%s\n", mysql_error(conn));
			gfs_rollback_metadata();
			return ECONN;
		}
		else 
		{
			sprintf(query, "UPDATE pid SET idmetadata=LAST_INSERT_ID() WHERE pid=%d", pid);
			if (mysql_query(conn, query))
			{
				fprintf(stderr, "%s\n", mysql_error(conn));
				gfs_rollback_metadata();
				return ECONN;
			}
			
			//COMMIT TRANSACTION
			if (mysql_commit(conn)!=0)
			{
				verbose("S:gfs_lockMetadata(): ERROR: failed to commit new booked metadata!");
				gfs_rollback_metadata();
				return ECONN;
			}
			return pid;		
		}
	}
	//Check if mode = 's', (create SHARED lock on existing file) ----------------------------S-----------------------
	else if (strcmp("s", lock_mode)==0)
	{
		//check whether file already exists-------------
		if (gfs_is_file_exist(item_name, formatted_logical_location)!=GFS_SUCCESS)
		{
			verbose("S:gfs_lockMetadata(): ERROR: File %s%s not exists.\n", formatted_logical_location, item_name);
			return EFNOTVALID;			
		}		
		strcpy(lock, "s");		
	}
	//Check if mode = 'x', (create EXCLUSIVE lock on existing file)------------------------------X-----------------------
	else if (strcmp("x", lock_mode)==0)
	{
		//check whether file already exists-------------
		if (gfs_is_file_exist(item_name, formatted_logical_location)!=GFS_SUCCESS)
		{
			verbose("S:gfs_lockMetadata(): ERROR: File %s%s not exists.\n", formatted_logical_location, item_name);
			return EFNOTVALID;			
		}		
		strcpy(lock, "x");
	}
	//Check if mode = 'xd', (create WRITE lock on existing directory)------------------------------W+-----------------------
	else if (strcmp("xd", lock_mode)==0)
	{
		if (gfs_isDirExist_p(item_name, formatted_logical_location)!=GFS_SUCCESS)
		{
			verbose("S:gfs_lockMetadata(): ERROR: Directory %s%s not exists.\n", formatted_logical_location, item_name);
			return EDNOTVALID;			
		}
		strcpy(lock, "x");
	}
	//Check if mode = 'sd', (create SHARED lock on existing directory
	else if (strcmp("sd", lock_mode)==0)
	{
		if (gfs_isDirExist_p(item_name, formatted_logical_location)!=GFS_SUCCESS)
		{
			verbose("S:gfs_lockMetadata(): ERROR: Directory %s%s not exists.\n", formatted_logical_location, item_name);
			return EDNOTVALID;			
		}
		strcpy(lock, "s");
	}
	//else, something is wrong
	else 
	{
		verbose("S:gfs_lockMetadata(): ERROR: Looped off the lock_mode condition. Lock_mode is: %s\n", lock_mode);
		return ERNS;
	}

	//STEP 4: Check permission ---------------------------PERMISSION---------------------------------
	if (access_mode!=NULL)
	{
		sprintf(permission_mode, "%c", access_mode[0]);
		if (access_mode[1]=='f' && gfs_get_permission(item_name, formatted_logical_location, permission_mode)!=GFS_SUCCESS)
		{
			verbose("S:gfs_lockMetadata(): ERROR: You don't have '%s' access to file %s%s.\n", permission_mode, formatted_logical_location, item_name);
			return EPERMIT;
		}
		else if (access_mode[1]=='d' && gfs_get_permission_location(formatted_logical_location, permission_mode)!=GFS_SUCCESS)
		{
			verbose("S:gfs_lockMetadata(): ERROR: You don't have '%s' access to dir %s%s.\n", permission_mode, formatted_logical_location, item_name);
			return EPERMIT;
		}
	}	
	
	//STEP 4: Create new PID in database and retrieve it ---------------------------PID---------------------------------
	sprintf(query, "INSERT INTO pid VALUES (NULL, (SELECT idmetadata FROM metadata WHERE filename='%s' AND logical_location='%s'))", item_name, formatted_logical_location);
	if (mysql_query(conn, query))
	{
		fprintf(stderr, "%s\n", mysql_error(conn));
		return ECONN;
	}
	else 
	{
		num_rows = mysql_affected_rows(conn);

		if (num_rows == 0)
		{
			verbose("S:gfs_lockMetadata(): ERROR: Failed creating new PID in database!\n");
			return ERNS;
		}
		else 
			pid=mysql_insert_id(conn);
	}
	
	//STEP 5: Set lock if lock is NULL (unlocked)
	sprintf(query,"UPDATE metadata SET locking='%s', lockid='%d' WHERE filename='%s' AND logical_location='%s' AND locking IS NULL", lock, pid, item_name, formatted_logical_location);
	if (mysql_query(conn, query))
	{
		fprintf(stderr, "%s\n", mysql_error(conn));
		gfs_deletePID(pid);
		return ECONN; 
	}
	
	num_rows = mysql_affected_rows(conn);

	//check whether update lock successful
	if (num_rows==0 && strcmp(lock, "x")==0)
	{
		verbose("S:gfs_lockMetadata(): Unable to get exclusive lock in %s%s\n", formatted_logical_location, item_name);
		gfs_deletePID(pid);
		return ELOCK;  //FAILED TO GET EXCLUSIVE LOCK!
	}
	else if (num_rows==0 && strcmp(lock, "s")==0)
	{
		sprintf(query,"SELECT locking FROM metadata WHERE filename='%s' AND logical_location='%s'", item_name, formatted_logical_location);
		if (mysql_query(conn, query))
		{
			fprintf(stderr, "%s\n", mysql_error(conn));
			gfs_deletePID(pid);
			return ECONN; 
		}
		res = mysql_store_result(conn);	
		num_rows = mysql_num_rows(res);
		row = mysql_fetch_row(res);
		if (strcmp(row[0], "s")==0)
			return pid; //SUCCESS GETTING READ LOCK ()
		else
		{
			verbose("S:gfs_lockMetadata(): Unable to get shared lock in %s%s\n", formatted_logical_location, item_name);
			gfs_deletePID(pid);
			return ELOCK; //FAILED TO GET READ LOCK
		}
	}
	else
		return pid; //SUCCESS GETTING EXCLUSIVE LOCK								
		

}
int gfs_deletePID(int pid)
{
	//delete PID
	sprintf(query, "DELETE FROM pid WHERE pid=%d", pid);
	if (mysql_query(conn, query)) 
	{
		fprintf(stderr, "%s\n", mysql_error(conn));
		return ECONN;
	}
	else 
	{
		return;
	}
}

/*********************************************************************************
* gfs_unlockMetadata
*
* @param:	pid: 
*			unbook: 1 or 0
*			item_name:
*			logical_loc:
* @return:	
*
*********************************************************************************/
int gfs_unlockMetadata(int pid, int unbook, char *item_name, char *logical_loc)
{
	char lock[3];
	
	//Open metadata database connection---------------------------------------
	if (gfs_open_metadata())
		verbose("S:gfs_unlockMetadata(): Metadata connection success\n");
	else
	{
		verbose("S:gfs_unlockMetadata(): ERROR: Metadata connection failed\n");
		return ECONN;
	}

	//Start a sql transaction
	strcpy(query, "START TRANSACTION");
	if (mysql_query(conn, query))
	{
		fprintf(stderr, "%s\n", mysql_error(conn));
		return ECONN;
	}

	//STEP 1: Check whether logical_path already follows proper naming of logical_location 
	//(needs '/' at the end of the full directory path)
	char formatted_logical_location[strlen(logical_loc)+1];
	gfs_format_logical_location(formatted_logical_location, logical_loc);

	//if unbook=1, delete the metadata
	if (unbook==1)
	{
		sprintf(query,"DELETE FROM metadata WHERE filename='%s' AND logical_location='%s' AND lockid=%d", item_name, formatted_logical_location, pid);

		if (mysql_query(conn, query)) 
		{
			fprintf(stderr, "%s\n", mysql_error(conn));
			gfs_rollback_metadata();
			return ECONN;
		}

		num_rows = mysql_affected_rows(conn);
		if (num_rows==0)
		{
			verbose("S:gfs_unlockMetadata(): ERROR: Unable to unbook %s%s with pid %d\n", formatted_logical_location, item_name, pid);
		}
		else
			verbose("S:gfs_unlockMetadata(): %s%s unbooked.\n", formatted_logical_location, item_name);
		
	}	
	else //unlock metadata
	{
		//get all lock and PID on item
		sprintf(query,"SELECT locking,pid FROM metadata JOIN pid ON metadata.idmetadata = pid.idmetadata WHERE filename='%s' AND logical_location='%s';", item_name, formatted_logical_location);
		if (mysql_query(conn, query)) 
		{
			fprintf(stderr, "%s\n", mysql_error(conn));
			gfs_rollback_metadata();
			return ECONN;
		}
		res = mysql_store_result(conn);	
		num_rows = mysql_num_rows(res);

		if (num_rows>1) //if there are another process working on the same item (such as in shared lock)
		{
			row = mysql_fetch_row(res);

			//get another pid to replace this pid
			while (pid==atoi(row[1]))
			{
				row = mysql_fetch_row(res);
			}
			sprintf(query,"UPDATE metadata SET lockid=%s WHERE filename='%s' AND logical_location='%s' AND lockid=%d", row[1], item_name, formatted_logical_location, pid);
			if (mysql_query(conn, query)) 
			{
				fprintf(stderr, "%s\n", mysql_error(conn));
				gfs_rollback_metadata();
				return ECONN;
			}
		}
		else //else, this is the only process on this item, and we can set the lock as NULL 
		{
			sprintf(query,"UPDATE metadata SET locking=NULL, lockid=NULL WHERE filename='%s' AND logical_location='%s' AND lockid=%d", item_name, formatted_logical_location, pid);

			if (mysql_query(conn, query)) 
			{
				fprintf(stderr, "%s\n", mysql_error(conn));
				gfs_rollback_metadata();
				return ECONN;
			}
			num_rows = mysql_affected_rows(conn);;
			if (num_rows==0)
			{
				// nothing unlocked
			}
		}

		//COMMIT TRANSACTION
		if (mysql_commit(conn)!=0)
		{
			verbose("S:gfs_unlockMetadata(): ERROR: failed to commit!");
			gfs_rollback_metadata();
			return ECONN;
		}
	}
	
	//delete PID
	sprintf(query, "DELETE FROM pid WHERE pid=%d", pid);
	if (mysql_query(conn, query)) 
	{
		fprintf(stderr, "%s\n", mysql_error(conn));
		return ECONN;
	}
	else 
	{	
		num_rows = mysql_affected_rows(conn);
		if (num_rows==0)
		{
			verbose("S:gfs_unlockMetadata(): ERROR : deleting PID %d\n", pid);
			return ECONN;
		}
		else 
		{
			gfs_close_metadata();
			return GFS_SUCCESS;
		}
	}
}
/*********************************************************************************
* gfs_getFileLocation
* 
* Note: - assuming file or directory IS exist.
* 
* @param: 
*
* @return 
*
*****************************************************************************************/
int gfs_getFileLocation(GFS_file file)
{
	verbose("S:getFileLocation() : mode : %s\n",file->mode);//debug
	verbose("S:getFileLocation() : file_name : %s\n",file->file_name);//debug
	verbose("S:getFileLocation() : logical_location : %s\n",file->logical_loc);//debug
	
	if (strchr(file->logical_loc,'/')==NULL || strcmp(file->file_name,"")==0 )
	{
		return EFNOTVALID;
	}

	//STEP 1 : Check whether logical_path already follows proper naming of logical_location 
	//(needs '/' at the end of the full directory path)
	char formatted_logical_location[strlen(file->logical_loc)+1];
	gfs_format_logical_location(formatted_logical_location, file->logical_loc);
	
	//STEP 2: Open metadata database connection-------------------------------------
	if (gfs_open_metadata())
		verbose("gfsServices.c : getFileLocation : connection success\n");
	else
		verbose("gfsServices.c : getFileLocation : connection failed\n");
	
	
	//STEP 3.1 : get location of existing file----------------------------------------------------
	if ( strcmp(file->mode, "r")==0 || strcmp(file->mode, "w+")==0 || strcmp(file->mode, "d")==0)
	{
		//STEP 3.1.1 : check whether file is already exists or not---------------
		if (gfs_is_file_exist(file->file_name, formatted_logical_location))
		{
			verbose("S:getFileLocation() : File exists. Retrieving file.\n");
			
			//get info from database (already queried in gfs_is_file_exist)
			row = mysql_fetch_row(res);
			file->file_id=atoi(row[0]);
			file->file_size=atoll(row[6]);

			//STEP 3.1.2 : Get Physical Location

			//create linked list of physical location
			file->physical_path=NULL;
			GFS_physical_path path_temp=NULL;
			GFS_physical_path path_tailPtr=NULL;

			//create linked list of server
			GFS_server server_headPtr=NULL;
			GFS_server server_temp=NULL;
			GFS_server server_tailPtr=NULL;

			//get physical_location(s) from database
			sprintf(query, "SELECT physical_location,server FROM physical_location JOIN gfs_server ON physical_location.idgfs_server=gfs_server.idgfs_server WHERE idmetadata=(SELECT idmetadata FROM metadata WHERE filename='%s' AND logical_location='%s')", file->file_name, formatted_logical_location);
			if (mysql_query(conn, query)) 
			{
				fprintf(stderr, "%s\n", mysql_error(conn));
				return ECONN;
			}
			else 
			{	
				res = mysql_store_result(conn);	
				num_rows = mysql_num_rows(res);
				row = mysql_fetch_row(res);

				while (row!=NULL)
				{					
					path_temp=malloc(sizeof(GFS_physical_pathD));
					server_temp=malloc(sizeof(GFS_serverD));
				
					strcpy(path_temp->url,row[0]);		//put url in physical path struct
					strcpy(server_temp->hostname,row[1]);		//put url in server struct
					
					path_temp->next=NULL;				//next is NULL
					server_temp->next=NULL;

					//put in linked list (PHYSICAL_PATH)
					if (file->physical_path==NULL)	
						file->physical_path=path_temp;
					else
						path_tailPtr->next=path_temp;
					path_tailPtr = path_temp;
					
					//put in linked list (server)
					if (server_headPtr==NULL)	
						server_headPtr=server_temp;
					else
						server_tailPtr->next=server_temp;
					server_tailPtr = server_temp;
										
					//advance to next record in database
					row = mysql_fetch_row(res);
				}
				
				//check server
				gfs_check_all_server(&server_headPtr);				

				//remove offline server from physical path
				path_temp=file->physical_path;
				server_temp=server_headPtr;
				GFS_physical_path path_prev=NULL;
				//char temp_path[QUERY_LENGTH];
				//char temp_hostname[FILENAME_LENGTH];
				while (server_temp!=NULL)
				{
					//strcpy(temp_path,path_temp->url); //get path
					//gfs_get_servername(now_src->url, servername); //get servername from path

					//remove offline server from the list
					if ( server_temp->online_status!=GFS_SUCCESS)
					{
						//remove offline server from the list
						if (path_prev!=NULL) //if not headPtr
						{
							path_prev->next=path_temp->next;
						}
						else //if headPtr
						{
							file->physical_path=file->physical_path->next;
						}	
					}
					else path_prev=path_temp;

					//advance both server and path					
					path_temp=path_temp->next;
					server_temp=server_temp->next;

				}

				//return if no server is online
				if (file->physical_path==NULL)
				{
					verbose("S:getFileLocation() : WARNING: No physical location available at this time!\n");
					return 0;
				}
				
				return GFS_SUCCESS;	
			}
		}
		else
		{
			verbose("S:getFileLocation() : ERROR: File %s does not exist\n", file->file_name);
			//gfs_close_metadata();
			return EFNOTVALID;
		}
	}
	//STEP 3.2 : 'w'  MODE (Get new physical location to put file on)--------------------------------------------------------
	else if (strcmp(file->mode, "w")==0)
	{	
		//STEP 3.2.1 : Separate between logical directory name in which the file is copied to and
		//           the location of that directory (confused?) 
		char dir_logical_location[PATH_LENGTH] = "" ;
		char dir_name[FILENAME_LENGTH] = "";
		gfs_get_location_and_dirname(file->logical_loc, dir_logical_location, dir_name);

		//STEP 3.2.2 : If all is well, then we get new physical location for the file
		verbose("S:getFileLocation() : OK. File does not exists. Proceed with put.\n");
		
		//STEP 3.2.5 : Get server with the most unused space from the database-----
		strcpy(query, "SELECT server, (total-used) AS unused FROM gfs_server ORDER BY unused DESC");
		char physical_filename[FILENAME_LENGTH];
		char physical_path[PATH_LENGTH];
		int mirror = 0;

		//RUN QUERY***
		//verbose("getLocation() : query : %s\n",query);//debug
		if (mysql_query(conn, query)) 
		{
			//FAILED: MySQL error
			fprintf(stderr, "%s\n", mysql_error(conn));
			return ECONN;
		}
		else 
		{
			//DETERMINE LOCATION-----------------------------------------------------------------------------------------------

			res = mysql_use_result(conn);	
			row = mysql_fetch_row(res);

			//create linked list of physical location
			GFS_physical_path physical_headPtr=NULL;
			GFS_physical_path physical_temp=NULL;
			GFS_physical_path physical_tailPtr=NULL;

			//create linked list of server
			GFS_server server_headPtr=NULL;
			GFS_server server_temp=NULL;
			GFS_server server_tailPtr=NULL;
			
			//Get server list from database
			while (row!=NULL)
			{
				strcpy(physical_path, row[0]);		//get server name from database
				
				//alocate memory
				server_temp=malloc(sizeof(GFS_serverD));	
				
				strcpy(server_temp->hostname, physical_path);	//get name
				server_temp->unused=atoi(row[1]);	//get unused space
				server_temp->next=NULL;
				
				/* not implemented yet
				if (file->file_size > (server_temp->unused)) //if file size is bigger than available space, 
				{
					//do nothing
				}
				else
				{
					//else add to list
				}*/

				//get the RTT (applicable if root)
				server_temp->rtt=gfs_icmp_ping(physical_path);

				//put in linked list
				if (server_headPtr==NULL)	
					server_headPtr=server_temp;
				else
					(server_tailPtr)->next=server_temp;
				server_tailPtr = server_temp;	

				row = mysql_fetch_row(res);
			}
			
			//Check server availability
			gfs_check_available_server(&server_headPtr);

			//Here we should sort the available server list based on RTT (deprecated, unnecessary)
			
			//create physical location url and add to linked list of physical path
			server_temp=server_headPtr;
			file->physical_path=NULL;
			while (server_temp!=NULL && mirror < MIRROR) //cek for mirror server quota
			{
				physical_temp=malloc(sizeof(GFS_physical_pathD));
							
				//create format : hostname/gfs/dir1_dir2_filename
				strcpy(physical_path, server_temp->hostname);
				strcat(physical_path, "/gfs/");

				//check if filename already used in the server (small probability of happening)				
				gfs_get_physical_filename(file->file_name, formatted_logical_location, physical_filename);
				char physical_filename_temp[strlen(physical_filename)+5];
				strcpy(physical_filename_temp, physical_filename);
				j=0;
				do
				{					
					sprintf(query, "select physical_location FROM physical_location WHERE physical_location LIKE '\%/%s' AND idgfs_server=(SELECT idgfs_server FROM gfs_server WHERE server='%s')", physical_filename_temp, server_temp->hostname);
					
					if (mysql_query(conn, query)) 
					{
						fprintf(stderr, "%s\n", mysql_error(conn));
						return ECONN;
					}
					else 
					{
						res = mysql_store_result(conn);		
						num_rows = mysql_num_rows(res);
						
						if (num_rows > 0)	//there is another file in database RNS!
						{
							sprintf(physical_filename_temp, "%s%d", physical_filename_temp, j);
						}				
						else if (num_rows==0 && j==0) ////no other file AT ALL
							break;
						else
							strcpy(physical_filename, physical_filename_temp);
					}
					j++;
				}
				while (num_rows > 0); //try to find new physical filename				

				strcat(physical_path, physical_filename);

				strcpy(physical_temp->url,physical_path);		//put in linked list

				physical_temp->next=NULL;
				
				//put in linked list
				if (file->physical_path==NULL)	
					file->physical_path=physical_temp;
				else
					(physical_tailPtr)->next=physical_temp;
				physical_tailPtr = physical_temp;	
				
				mirror++;
				server_temp=server_temp->next;
			}
			//END OF DETERMINING LOCATION---------------------------------------------------------------------------------
			
			//check success retrieving location
			if (file->physical_path==NULL)
			{
				verbose("S:getFileLocation() : WARNING: Failed getting physical location. Check server status!\n");
				return 0;
			}
			
			//free lists of server pointer
			server_temp=server_headPtr;
			while (server_temp!=NULL)
			{
				GFS_server server_to_free=server_temp;
				server_temp=server_temp->next;				
				free(server_to_free);
			}
			
			//file->physical_path=physical_headPtr;	//assign physical_location to file descriptor		

			return GFS_SUCCESS;
		
		}
	}
	
	verbose("S:getFileLocation() : something is wrong! file->mode=%s\n", file->mode);//debug
	return -2;
}

/*********************************************************************************
* gfs_getFileLocationMove
* 
* @param: 
*
* @return	:	return 0 if failed to get new location (false)
*				return 1 if OK
*****************************************************************************************/
int gfs_getFileLocationMove(GFS_file src_file, GFS_file dst_file)
{
	verbose("S:gfs_getFileLocationMove() : old_filename : %s\n",src_file->file_name);//debug
	verbose("S:gfs_getFileLocationMove() : old_logical_location : %s\n",src_file->logical_loc);//debug
	verbose("S:gfs_getFileLocationMove() : new_filename : %s\n",dst_file->file_name);//debug
	verbose("S:gfs_getFileLocationMove() : new_logical_location : %s\n",dst_file->logical_loc);//debug
	
	char formatted_new_logical_location[strlen(dst_file->logical_loc)+1];
	gfs_format_logical_location(formatted_new_logical_location,dst_file->logical_loc);
	
	//First, we get physical location of the old file
	int result=gfs_getFileLocation(src_file);
	
	if (result==GFS_SUCCESS)
	{
		verbose("gfs_getFileLocationMove() : Success retrieving old_physical_path\n");//debug
		
		//file not exists, and we can proceed with moving, so we create a new physical path on the same server
		char servername[FILENAME_LENGTH];
		char physical_filename[FILENAME_LENGTH];

		//create linked list of physical location
		GFS_physical_path now_src=src_file->physical_path;
		GFS_physical_path headPtr=NULL;
		GFS_physical_path now_dst;
		GFS_physical_path tailPtr=NULL;
		
		while (now_src!=NULL)
		{
			now_dst=malloc(sizeof(GFS_physical_pathD));

			//get servername from the old physical path
			gfs_get_servername(now_src->url, servername);
			
			strcpy(now_dst->url, servername);
			strcat(now_dst->url, "/gfs/");
			gfs_get_physical_filename(dst_file->file_name, formatted_new_logical_location, physical_filename);
			strcat(now_dst->url, physical_filename);
			verbose("gfs_getFileLocationMove() : Success generating new_physical_path : %s\n",now_dst->url);//debug

			now_dst->next=NULL;
			
			//put in linked list
			if (headPtr==NULL)	
				headPtr=now_dst;
			else
				(tailPtr)->next=now_dst;
			tailPtr = now_dst;
		
			//get next item
			now_src=now_src->next;
		}
		
		dst_file->file_size=src_file->file_size;
		dst_file->physical_path = headPtr;

		return GFS_SUCCESS;
	}
	else
	{
		verbose("getLocationMove() : cannot read source file.\n");
		return result;
	}
	
}

/********************************************************************************
* gfs_getItemList 
* Called by LS:
* - check whether directory is valid (using isDirExist())
* - returns number of files and directory in a directory
*
* @param:	logical_path	: Logical Path of directory 
* @returns:	number of rows
*			0 if directory is empty
*			EDNOTVALID if directory is not valid
*			-2 if something is wrong
************************************************************************************/
int gfs_getItemList(GFS_dir dir, GFS_item *item)
{
	verbose("gfs_getItemList() : dir_name : %s\n", dir->dir_name);//debug
	verbose("gfs_getItemList() : logical_location : %s\n", dir->logical_loc);//debug
	
	//creates logical location string which slightly longer than the logical path
	char formatted_logical_location[strlen(dir->logical_loc)+1];
	char full_dir_path[PATH_LENGTH];
		
	//check whether logical_path already follows proper naming of logical_location 
	//(needs '/' at the end of the full directory path)
	gfs_format_logical_location(formatted_logical_location, dir->logical_loc);
	
	/*/separate dir name and location if logical_path is not root
	if (strcmp(logical_path, "/")!=0)
		gfs_get_location_and_dirname(formatted_logical_location, dir_logical_location, dir_name);
	else
		strcpy(dir_logical_location, logical_path);*/
	
	//open metadata database connection
	if (gfs_open_metadata())
		verbose("gfs_getItemList() : connection success\n");
	else
		verbose("gfs_getItemList() : connection failed\n");
	
	//checking directory path validity
	if (gfs_isDirExist(dir))
	{
		verbose("getFileList() : directory is exists\n");
	}
	else
	{
		//Directory is NOT valid, return
		verbose("getFileList() : directory does not exist\n");
		return EDNOTVALID;
	}
	
	//concate location and directory name
	if (strcmp(formatted_logical_location, "/")==0 && strcmp(dir->dir_name, "")==0)
	{
		strcpy(full_dir_path, formatted_logical_location);		
	}
	else 
		sprintf(full_dir_path, "%s%s/", formatted_logical_location, dir->dir_name);


	//BUILD QUERY***
	sprintf(query, "SELECT * FROM metadata WHERE logical_location='%s'", full_dir_path);
	
	//RUN QUERY***
	verbose("getFileList() : query : %s\n",query);//debug
	if (mysql_query(conn, query)) 
	{
		//FAILED: MySQL error
		fprintf(stderr, "%s\n", mysql_error(conn));
		return ECONN;
	}
	else 
	{
		verbose("getFileList() : retrieving file list from DB\n");
		res = mysql_store_result(conn);		
		num_rows = mysql_num_rows(res);

		GFS_item temp;
		GFS_item tailPtr=NULL;
		char tempStr[FILENAME_LENGTH];
		
		while ((row = mysql_fetch_row(res)) != NULL)
		{
			temp=malloc(sizeof(GFS_itemD));
			if (temp!=NULL)
    	    {

				strcpy(temp->item_name,row[1]);
				strcpy(temp->created_date,row[3]);
				strcpy(temp->modified_date,row[4]);
				strcpy(temp->type,row[5]);
				//if it is folder, then size is null, and we assign it as 0:
				if (row[6]!=NULL)
					temp->size=atoll(row[6]);
				else temp->size=0;

				if (row[7]!=NULL)
					strcpy(temp->owner,row[7]);
				else 
					strcpy(temp->owner,"NULL");

				if (row[8]!=NULL)
					gfs_permission_bin2str(row[8],tempStr);
				else 
					strcpy(tempStr,"------");
				strcpy(temp->permission,tempStr);
				temp->next=NULL;

				if ((*item)==0x0)	
					(*item)=temp;
    		    else
					(tailPtr)->next=temp;
       		    tailPtr = temp;			
			}
    	    else 
			{
				verbose("getFileList() : Out of Memory.\n");
				return ERNS;
			}			
		}

		verbose("getFileList() : returning to main.\n");
		//gfs_close_metadata();
		return num_rows;
	}
	
	//something is wrong
	//gfs_close_metadata();
	return -2;

}

/**************************************************************************
* isDirExist
* Called by CD , RMDIR, MKDIR:
* - check whether directory is valid (read from database)
* - return flag => 0 if invalid, 1 if valid 
*
* @param	:	logical_path	: Logical Path of directory 
* @return	:	return 0 if directory DO NOT exist (false)
*				return 1 if directory exist
***************************************************************************/
int gfs_isDirExist(GFS_dir dir)
{
	//open metadata database connection
	if (gfs_open_metadata())
		verbose("isDirExist() : connection success\n");
	else
		verbose("isDirExist() : connection failed\n");

	int result=gfs_isDirExist_p(dir->dir_name, dir->logical_loc);

	return result;
}
int gfs_isDirExist_p(char *dir_name, char *logical_location)
{
	//base case: if root, then it is exist
	if (strcmp(logical_location, "/")==0 && strcmp(dir_name,"")==0)
		return 1;
	
	//char logical_location[PATH_LENGTH] = "" ;
	char formatted_logical_location[PATH_LENGTH] = "" ;
	verbose("isDirExist() : logical_location : %s\n", formatted_logical_location);//debug
	verbose("isDirExist() : dir_name : %s\n", dir_name);//debug	
	
	//check whether logical_location already follows proper naming of logical_location 
	//(needs '/' at the end of the full directory path)
	gfs_format_logical_location(formatted_logical_location, logical_location);

	//BUILD QUERY***
	char query[QUERY_LENGTH] = "SELECT * FROM metadata WHERE logical_location='";
	strcat(query, formatted_logical_location);
	strcat(query, "' AND filename='");
	strcat(query, dir_name);
	strcat(query, "' AND type='d'");
	
	//RUN QUERY***
	verbose("isDirExist() : query : %s\n",query);//debug
	if (mysql_query(conn, query)) 
	{
		fprintf(stderr, "%s\n", mysql_error(conn));
		return(0);
	}
	else 
	{
		res = mysql_store_result(conn);
		num_rows = mysql_num_rows(res);
		
		if (num_rows == 0)
			return 0;
		else
			return GFS_SUCCESS;
	}

	return -1;
}

/****************************************************************************
* chmod 
* change permission information on file or directory
*
* @param :	filename	: name of the file/directory to change
*			logical_location  : logical location of the file/directory. Do not forget '/' at the beginning and the end  ( i.e: /dir1/ ) 
* 			mode		: octal integer of the information, ex: 64 for rw-r--, or 75 for rwxr-x.
* @return :	return 0 if nothing written into database
*			return 1 if success 
*****************************************************************************/
int gfs_change_permission(char *item_name, char *logical_location, char *mode)
{
	verbose("chmod() : filename : %s\n", item_name);
	verbose("chmod() : logical_location : %s\n", logical_location);
	verbose("chmod() : mode : %s\n", mode);
	
	//check input mod
	if (atoi(mode)<0 || atoi(mode)>77)
	{
		return 0;
	}

	//get user name
	char user[FILENAME_LENGTH];
	cuserid(user);
	//convert mode to binary (ex: 64 to 110100)
	char bin_str[8];
	strcpy(bin_str, "");
	char temp_str[4];
	strcpy(temp_str, "");
	int mode_temp=0,x=0,y=0,pos=0;
	while (pos<2)
	{
		//sprintf(temp_str,"%d",mode);
		strcpy(temp_str,mode);
		//if length is longer than 2 digit, not valid 
		if (strlen(temp_str)>2) return 0;
		mode_temp =temp_str[pos]-48;
		for (y=2; y>=0; y--)
		{
			x=mode_temp/(1<<y);
			mode_temp=mode_temp-x*(1<<y);
			sprintf(temp_str,"%d",x);
			strcat(bin_str,temp_str);
		}
		pos++;
	}
	verbose("chmod() : bin_str : %s", bin_str);
	
	//open metadata database connection
	if (gfs_open_metadata())
		verbose("chmod() : connection success\n");
	else
	{
		verbose("chmod() : connection failed\n");
		return 0;
	}
	
	//check whether logical_location already follows proper naming of logical_location 
	//(needs '/' at the end of the full directory path)
	char logical_location_temp[strlen(logical_location)+1];
	gfs_format_logical_location(logical_location_temp, logical_location);

	if (gfs_isDirExist_p(item_name, logical_location_temp)==GFS_FAILED && gfs_is_file_exist(item_name, logical_location_temp)==GFS_FAILED)
	{
		return EFDNOTVALID;
	}
	
	//check owner
	sprintf(query, "SELECT owner,permission FROM metadata WHERE filename='%s' AND logical_location='%s'", item_name, logical_location_temp);
	//run query
	if (mysql_query(conn, query))
	{
		fprintf(stderr, "%s\n", mysql_error(conn));
		return -1;
	}
	res = mysql_store_result(conn);
	row= mysql_fetch_row(res);
	if (strcmp(row[0],user)!=0)
	{
		//if not owner
		return EPERMIT;
	}
	if (strcmp(row[1],bin_str)==0)
	{
		return GFS_SUCCESS;
	}
	
	//create query
	sprintf(query, "UPDATE metadata SET permission='%s' WHERE filename='%s' AND logical_location='%s' AND owner='%s'"
		, bin_str, item_name, logical_location_temp, user );
	
	
	verbose("chmod() : query : %s\n", query);
	//run query
	if (mysql_query(conn, query))
	{
		fprintf(stderr, "%s\n", mysql_error(conn));
		return 0;
	}
	else 
	{
		num_rows = mysql_affected_rows(conn);
		verbose("chmod() : affected rows: %d\n", num_rows);

		//gfs_close_metadata();
		return num_rows;					
	}	
	
	
}
/****************************************************************************
* gfs_saveFileMetadata 
* write passed file metadata to the database
*
* @param :	mode		: "w" or "d"
*			filename	: name of the file/directory to write to database
*			logical_location  : logical location of the file/directory. Do not forget '/' at the beginning and the end  ( i.e: /dir1/ ) 
*			physical_location : physical location of the file/directory (NULL if mode=mkdir)
* @return :	return 0 if failed writing to database
*			return 1 if success 
*****************************************************************************/
int gfs_saveFileMetadata(GFS_file file)
{
	//creates logical location string which slightly longer than the logical path
	char formatted_logical_location[strlen(file->logical_loc)+1];
	GFS_physical_path now;

	verbose("writeMetadata() : mode : %s\n", file->mode);
	verbose("writeMetadata() : filename : %s\n", file->file_name);
	verbose("writeMetadata() : logical_location : %s\n", file->logical_loc);
	//verbose("writeMetadata() : physical_location : %s", physical_location);
	verbose("writeMetadata() : size : %lld\n", file->file_size);

	//get username (owner of the file)
	char user[FILENAME_LENGTH];
	//strcpy(user, getenv("USER"));
	cuserid(user);

	//get default user permission from gfs.conf
	char permissionStr[16];
	gfs_get_conf_file_section(CONF_FILENAME, "default_file_permission", permissionStr);
	if (permissionStr==NULL || strlen(permissionStr)!=6)
		strcpy(permissionStr,"110100");
	
	verbose("writeMetadata() : user : %s\n", user);
	verbose("writeMetadata() : permission : %s\n", permissionStr);

	//check whether logical_location already follows proper naming of logical_location 
	//(needs '/' at the end of the full directory path)
	gfs_format_logical_location(formatted_logical_location, file->logical_loc);	

	//open metadata database connection
	if (gfs_open_metadata())
		;
	else
		return 0;

	//start a sql transaction
	strcpy(query, "START TRANSACTION");
	if (mysql_query(conn, query))
	{
		fprintf(stderr, "%s\n", mysql_error(conn));
		return ECONN;
	}
	
	if (strcmp(file->mode, "w")==0 || strcmp(file->mode, "w+")==0) //mode 'w'-----------------------------------------W--------------------------------------------
	{
		//BUILD QUERY***
		sprintf(query, "UPDATE metadata SET created_date=NOW(), modified_date=NOW(), type='f', size=%lld, owner='%s', permission='%s' WHERE filename='%s' AND logical_location='%s'", file->file_size, user, permissionStr, file->file_name, formatted_logical_location );
		
			
		//RUN QUERY***
		verbose("writeMetadata() : query : %s\n",query);//debug
		if (mysql_query(conn, query)) 
		{
			fprintf(stderr, "%s\n", mysql_error(conn));
			gfs_rollback_metadata();
			return ECONN;
		}
		else 
		{
			num_rows = mysql_affected_rows(conn);
			verbose("writeMetadata() : affected rows: %d\n", num_rows);
			

			if (num_rows == 0) //if nothing is updated, something is wrong
			{
				gfs_rollback_metadata();
				return ERNS;
			}
			else
			{
				//get first physical_path in file descriptor
				now = file->physical_path;

				char servername_temp[512];
				
				while (now!=NULL)
				{
					//get servername
					gfs_get_servername(now->url, servername_temp);
					
					if (strcmp(file->mode, "w")==0)
					{
						//insert physical location
						sprintf(query, "INSERT INTO physical_location (idmetadata, physical_location, idgfs_server) VALUES ((SELECT idmetadata FROM metadata WHERE filename='%s' AND logical_location='%s'), '%s', (SELECT idgfs_server FROM gfs_server WHERE server='%s'));", file->file_name, formatted_logical_location, now->url, servername_temp);
						if (mysql_query(conn, query)) 
						{
							fprintf(stderr, "%s\n", mysql_error(conn));
							gfs_rollback_metadata();
							return ECONN;
						}
					}					
					
					//if insert physical location success, update the disk usage counter in the database									
					sprintf(query, "UPDATE gfs_server SET used=used+%lld WHERE server='%s'", file->file_size, servername_temp);
					//verbose("writeMetadata() : query2 : %s\n",query);
					if (mysql_query(conn, query))
					{
						fprintf(stderr, "%s\n", mysql_error(conn));
						gfs_rollback_metadata();
						return ECONN;
					}
					
					//go to next physical path
					now=now->next;
				}

				//and then we update the modified date of the logical folder
				if (strcmp(file->mode, "w+")==0)
				{
					gfs_update_mod_date_abs_path(formatted_logical_location);
				}
				//or the file itself
				else if (strcmp(file->mode, "w+")==0)
				{
					gfs_update_mod_date(file->file_name, formatted_logical_location);
				}
				
				//COMMIT TRANSACTION
				if (mysql_commit(conn)!=0)
				{
					verbose("S:ERROR: failed to commit database!");
					gfs_rollback_metadata();
					return ECONN;
				}

				//and return success
				return GFS_SUCCESS;
			}
		}
	}
	else if (strcmp(file->mode, "d")==0) //mode "rm"------------------------------------DELETE-------------------------------------
	{
		char servername[128];
		GFS_physical_path now;

		if (file->physical_path==NULL)
		{
			gfs_rollback_metadata();
			return 0;
		}

		//get physical location from file descriptor
		now=file->physical_path;

		while (now!=NULL)
		{
			//get servername from physical location
			gfs_get_servername(now->url, servername);

			//we subtract file size from the used space in the database
			sprintf(query, "UPDATE gfs_server SET used=used-%lld WHERE server='%s'", file->file_size, servername);

			if (mysql_query(conn, query)) 
			{
				fprintf(stderr, "%s\n", mysql_error(conn));
				gfs_rollback_metadata();
				return ECONN;
			}
			else
				//go to next physical path
				now=now->next;
		}

		//BUILD QUERY***
		sprintf(query, "DELETE FROM metadata WHERE filename='%s' AND logical_location='%s'", file->file_name, formatted_logical_location);
				
		//RUN QUERY***
		//verbose("deleteMetadata() : query : %s\n",query);//debug
		if (mysql_query(conn, query)) 
		{
			fprintf(stderr, "%s\n", mysql_error(conn));
			gfs_rollback_metadata();
			return(0);
		}
		else 
		{
			num_rows = mysql_affected_rows(conn);
			verbose("deleteMetadata() : affected rows: %d\n", num_rows);			

			if (num_rows == 0)
			{				
				gfs_rollback_metadata();
				return 0;
			}
			else
			{
				gfs_update_mod_date_abs_path(formatted_logical_location);
				
				//COMMIT TRANSACTION
				if (mysql_commit(conn)!=0)
				{
					verbose("S:ERROR: failed to commit database!");
					gfs_rollback_metadata();
					return ECONN;
				}
				return num_rows;
			}
		}
	}	
}

int gfs_saveDirMetadata(GFS_dir dir)
{
	//creates logical location string which slightly longer than the logical path
	char formatted_logical_location[strlen(dir->logical_loc)+1];
	GFS_physical_path now;

	verbose("gfs_saveDirMetadata() : mode : %s\n", dir->mode);
	verbose("gfs_saveDirMetadata() : filename : %s\n", dir->dir_name);
	verbose("gfs_saveDirMetadata() : logical_location : %s\n", dir->logical_loc);
	//verbose("writeMetadata() : physical_location : %s", physical_location);

	//get user environment (owner of the file)
	char user[FILENAME_LENGTH];
	//strcpy(user, getenv("USER"));
	cuserid(user);

	//get default user permission 
	char permissionStr[16];
	gfs_get_conf_file_section(CONF_FILENAME, "default_dir_permission", permissionStr);
	if (permissionStr==NULL || strlen(permissionStr)!=6)
		strcpy(permissionStr,"111101");
	
	verbose("gfs_saveDirMetadata() : user : %s\n", user);
	verbose("gfs_saveDirMetadata() : permission : %s\n", permissionStr);

	//check whether logical_location already follows proper naming of logical_location 
	//(needs '/' at the end of the full directory path)
	gfs_format_logical_location(formatted_logical_location, dir->logical_loc);

	//open metadata database connection
	if (gfs_open_metadata())
		verbose("gfs_saveDirMetadata() : connection success\n");
	else
	{
		verbose("gfs_saveDirMetadata() : connection failed\n");
		return 0;
	}

	//start a sql transaction
	strcpy(query, "START TRANSACTION");
	if (mysql_query(conn, query))
	{
		fprintf(stderr, "%s\n", mysql_error(conn));
		return ECONN;
	}
	
	if (strcmp(dir->mode, "w")==0) //mode -----------------------------------------MKDIR--------------------------------------------
	{
		//BUILD QUERY***
		sprintf(query, "UPDATE metadata SET created_date=NOW(), modified_date=NOW(), type='d', owner='%s', permission='%s' WHERE filename='%s' AND logical_location='%s'", user, permissionStr, dir->dir_name, formatted_logical_location );
		
			
		//RUN QUERY***
		//verbose("gfs_saveDirMetadata() : query : %s\n",query);//debug
		if (mysql_query(conn, query)) 
		{
			fprintf(stderr, "%s\n", mysql_error(conn));
			gfs_rollback_metadata();
			return ECONN;
		}
		else 
		{
			num_rows = mysql_affected_rows(conn);
			verbose("gfs_saveDirMetadata() : affected rows: %d\n", num_rows);			

			if (num_rows == 0) //if nothing is updated, something is wrong
			{
				gfs_rollback_metadata();
				return ERNS;
			}
			else
			{
				//and then we update the modified date of the logical folder
				gfs_update_mod_date_abs_path(formatted_logical_location);

				dir->process_status=GFS_SUCCESS;
				
				//COMMIT TRANSACTION
				if (mysql_commit(conn)!=0)
				{
					verbose("S:ERROR: failed to commit database!");
					gfs_rollback_metadata();
					return ECONN;
				}

				//and return success
				return GFS_SUCCESS;
			}
		}
	}
	else if (strcmp(dir->mode, "d")==0) //mode "rm"------------------------------------RMDIR-------------------------------------
	{
		//BUILD QUERY***
		sprintf(query, "DELETE FROM metadata WHERE filename='%s' AND logical_location='%s'", dir->dir_name, formatted_logical_location);
				
		//RUN QUERY***
		//verbose("deleteMetadata() : query : %s\n",query);//debug
		if (mysql_query(conn, query)) 
		{
			fprintf(stderr, "%s\n", mysql_error(conn));
			gfs_rollback_metadata();
			return(0);
		}
		else 
		{
			num_rows = mysql_affected_rows(conn);
			verbose("gfs_saveDirMetadata() : affected rows: %d\n", num_rows);
			
			if (num_rows == 0)
			{
				gfs_rollback_metadata();
				return 0;
			}
			else
			{
				gfs_update_mod_date_abs_path(formatted_logical_location);
				dir->process_status=GFS_SUCCESS;
				
				//COMMIT TRANSACTION
				if (mysql_commit(conn)!=0)
				{
					verbose("S:ERROR: failed to commit database!");
					gfs_rollback_metadata();
					return ECONN;
				}
				return GFS_SUCCESS;
			}
		}
	}
	
}

/****************************************************************************
* gfs_moveFileMetadata
* find a metadata based on old_filename and old_logical_location and change them into 
* new_filename, new_logical_location, and new_physical_location.
*
* @param :	
* @return :	< 1 if failed
*		GFS_SUCCESS affected if succeed
*****************************************************************************/
int gfs_moveFileMetadata(GFS_file src_file, GFS_file dst_file)
{
	verbose("moveMetadata() : old_filename : %s\n", src_file->file_name);
	verbose("moveMetadata() : old_logical_location : %s\n", src_file->logical_loc);
	verbose("moveMetadata() : new_filename : %s\n", dst_file->file_name);
	verbose("moveMetadata() : new_logical_location : %s\n", dst_file->logical_loc);

	char formatted_src_location[PATH_LENGTH];
	char formatted_dst_location[PATH_LENGTH];
	//adds '/' to the end of log loc:
	gfs_format_logical_location(formatted_src_location, src_file->logical_loc);
	gfs_format_logical_location(formatted_dst_location, dst_file->logical_loc);
	
	//open metadata database connection
	if (gfs_open_metadata())
		verbose("moveMetadata() : connection success\n");
	else
	{
		verbose("moveMetadata() : connection failed\n");
		return ECONN;
	}	

	//start a sql transaction
	strcpy(query, "START TRANSACTION");
	if (mysql_query(conn, query))
	{
		fprintf(stderr, "%s\n", mysql_error(conn));
		return ECONN;
	}

	//remove booked metadata
	sprintf(query, "DELETE FROM metadata WHERE filename='%s' AND logical_location='%s'", dst_file->file_name, formatted_dst_location);
	if (mysql_query(conn, query))
	{
		fprintf(stderr, "%s\n", mysql_error(conn));
		gfs_rollback_metadata();
		return ECONN;
	}

	num_rows = mysql_affected_rows(conn);

	if (num_rows<1)
	{
		gfs_rollback_metadata();
		return ERNS;
	}
	
	//alter existing node
	sprintf(query, "UPDATE metadata SET filename='%s', logical_location='%s' WHERE filename='%s' AND logical_location='%s'", dst_file->file_name, formatted_dst_location, src_file->file_name, formatted_src_location );
	if (mysql_query(conn, query))
	{
		fprintf(stderr, "%s\n", mysql_error(conn));
		gfs_rollback_metadata();
		return ECONN;
	}
	
	num_rows = mysql_affected_rows(conn);

	if (num_rows<1)
	{
		gfs_rollback_metadata();
		return ERNS;
	}

	GFS_physical_path now;
	char servername_temp[512];
	now=dst_file->physical_path;

	while (now!=NULL)
	{
		//get servername
		gfs_get_servername(now->url, servername_temp);
		//sprintf(query, "UPDATE physical_location SET physical_location='%s' WHERE physical_location='%s'", now->url, );
		sprintf(query, "UPDATE physical_location SET physical_location='%s' WHERE idmetadata=(SELECT idmetadata FROM metadata WHERE filename='%s' AND logical_location='%s') AND idgfs_server=(SELECT idgfs_server FROM gfs_server WHERE server='%s');", now->url, dst_file->file_name, formatted_dst_location, servername_temp);
		if (mysql_query(conn, query))
		{
			fprintf(stderr, "%s\n", mysql_error(conn));
			gfs_rollback_metadata();
			return ECONN;
		}
		now=now->next;
	}
					
		
	//if you got this far, it means: 
	//1)booked metadata already deleted; 2)the destination metadata is now resides in old source metadata row, and has source pid
	//so it is safe to switch the PIDs so that both PID can get unlocked in the library.
	int temp;
	temp=dst_file->pid;
	dst_file->pid=src_file->pid;
	src_file->pid=temp;

	gfs_update_mod_date_abs_path(formatted_dst_location);
	gfs_update_mod_date_abs_path(formatted_src_location);

	//COMMIT TRANSACTION
	if (mysql_commit(conn)!=0)
	{
		verbose("S:ERROR: failed to commit database!");
		gfs_rollback_metadata();
		return ECONN;
	}
	
	return GFS_SUCCESS;					
		
}

int gfs_moveDirMetadata(GFS_dir src_dir, GFS_dir dst_dir)
{
	verbose("moveMetadata() : old_filename : %s\n", src_dir->dir_name);
	verbose("moveMetadata() : old_logical_location : %s\n", src_dir->logical_loc);
	verbose("moveMetadata() : new_filename : %s\n", dst_dir->dir_name);
	verbose("moveMetadata() : new_logical_location : %s\n", dst_dir->logical_loc);

	char formatted_src_location[PATH_LENGTH];
	char formatted_dst_location[PATH_LENGTH];
	//adds '/' to the end of log loc:
	gfs_format_logical_location(formatted_src_location, src_dir->logical_loc);
	gfs_format_logical_location(formatted_dst_location, dst_dir->logical_loc);
	
	//open metadata database connection
	if (gfs_open_metadata())
		verbose("moveMetadata() : connection success\n");
	else
	{
		verbose("moveMetadata() : connection failed\n");
		return ECONN;
	}
	
	//start a sql transaction
	strcpy(query, "START TRANSACTION");
	if (mysql_query(conn, query))
	{
		fprintf(stderr, "%s\n", mysql_error(conn));
		return ECONN;
	}

	//get idmetadata of source
	sprintf(query, "DROP TEMPORARY TABLE IF EXISTS metadata_tmp");
	if (mysql_query(conn, query))
	{
		fprintf(stderr, "%s\n", mysql_error(conn));
		gfs_rollback_metadata();
		return ECONN;
	}
	sprintf(query, "CREATE TEMPORARY TABLE metadata_tmp AS SELECT created_date,modified_date,owner,permission FROM metadata WHERE filename='%s' AND logical_location='%s'", src_dir->dir_name, formatted_src_location);
	if (mysql_query(conn, query))
	{
		fprintf(stderr, "%s\n", mysql_error(conn));
		gfs_rollback_metadata();
		return ECONN;
	}
		
	//alter existing booked node
	sprintf(query, "UPDATE metadata AS dst, metadata_tmp AS src SET dst.created_date=src.created_date, dst.modified_date=src.modified_date, dst.owner=src.owner, dst.permission=src.permission \
		WHERE filename='%s' AND logical_location='%s'", dst_dir->dir_name, formatted_dst_location);
	if (mysql_query(conn, query))
	{
		fprintf(stderr, "%s\n", mysql_error(conn));
		gfs_rollback_metadata();
		return ECONN;
	}
		
	gfs_update_mod_date_abs_path(formatted_dst_location);
	gfs_update_mod_date_abs_path(formatted_src_location);

	//commit!		
	if (mysql_commit(conn)!=0)
	{
		verbose("moveMetadata : failed commit");
		gfs_rollback_metadata();
		return ERNS;
	}

	
	
	return GFS_SUCCESS;					
		
}

/****************************************************************************
* gfs_open_metadata
* initialize metadata connection
*
* 
* @param: 
* @return:  0 if failed, 1 if succeed
*****************************************************************************/
int gfs_init_metadata()
{
	if (gfs_read_conf_file(CONF_FILENAME)!=GFS_SUCCESS)
	{
		//failed opening conf file
		return -1;
	}

	return gfs_open_metadata();
}
int gfs_open_metadata()
{
	if (conn==NULL || conn->user==NULL) //if connection is closed or 
	{
		if (strcmp(server,"")==0)
		{
			return 0;
		}

		conn = mysql_init(NULL);

		/* Connect to database */
		verbose("gfs_open_metadata() : initializing connection\n");

		if (!mysql_real_connect(conn, server, user, password, database, 0, NULL, 0)) 
		{
			fprintf(stderr, "%s\n", mysql_error(conn));
			return ERNS;
		}
		
		//dicek supaya kalo koneksi gagal ngembaliin ERNS! gimana coba
		if (conn==NULL || conn->user==NULL)
		{
			return ERNS;
		}

		return 1;
	}
	
	return 1;	
}

/****************************************************************************
* gfs_close_metadata
* close metadata database connection and free result
* WARNING : make sure MYSQL_RES *res contains something before freeing it.
* 
* @param: 
* @return: 
****************************************************************************/
void gfs_close_metadata()
{	
	mysql_close(conn);
}



/****************************************************************************
* gfs_is_file_exist
*  
*
* @param: 
* @return: 0 if file does not exist. Other than 0 if file exists.
*****************************************************************************/
int gfs_is_file_exist(char *filename, char *logical_location)
{
	//verbose("gfs_is_file_exist() ; param: %s\n",logical_path);//debug
	//char logical_location[PATH_LENGTH] = "" ;
	//char filename[FILENAME_LENGTH] = "";
	
	//get_path_and_filename(logical_path, logical_location, filename);

	verbose("gfs_is_file_exist() : location: %s\n",logical_location);
	verbose("gfs_is_file_exist() : filename: %s\n",filename);//debug
	
	char formatted_logical_location[strlen(logical_location)];
	gfs_format_logical_location(formatted_logical_location, logical_location);

	//BUILD QUERY***
	char query[QUERY_LENGTH] = "SELECT * FROM metadata WHERE logical_location='";
	strcat(query, formatted_logical_location);
	strcat(query, "' AND filename='");
	strcat(query, filename);
	strcat(query, "' AND type='f'");
	
	//RUN QUERY***
	//verbose("gfs_is_file_exist() : query : %s",query);//debug
	if (mysql_query(conn, query)) 
	{
		fprintf(stderr, "%s\n", mysql_error(conn));
		return(0);
	}
	else 
	{
		
		res = mysql_store_result(conn);
		num_rows = mysql_num_rows(res);

		if (num_rows == 0)
			return 0;
		else
			return GFS_SUCCESS;
	}
}

/******************************************************************************************
* get_path_and_filename
* Separate file location and filename based on the last '/' (directory divider)
* "/dir1/dir2/name.txt" to "/dir1/dir2/" and "name.txt"
*
* @param: logical_path (complete), logical_location (path) , and filename
* @return: void
******************************************************************************************/
int get_path_and_filename(char *logical_path, char *logical_location, char *filename)
{
	char *filenamePtr;

	//get filename
	filenamePtr=strrchr(logical_path,'/');			//get last occurences of char '/'
	if (filename!=NULL)
		strcpy(filename,filenamePtr+1);					//get file name

	//get path (logical location)
	for (i=0; i<=(filenamePtr-logical_path) ;i++ )
	{
		logical_location[i]=logical_path[i];		//move path to logical_location
	}
	logical_location[i]=0x00;
}

/******************************************************************************************
* gfs_get_location_and_dirname
* Separate directory location and directory name based on the last '/' (directory divider)
* "/dir1/dir2/dir3" to "/dir1/dir2/" and "dir3" OR
* "/dir1/dir2/dir3/" to "/dir1/dir2/" and "dir3"
*
* @param: logical_path (complete), logical_location (path) , and filename
* @return: void
******************************************************************************************/
int gfs_get_location_and_dirname(char *logical_path, char *logical_location, char * dirname)
{
	char *dirnamePtr;
	
	strcpy(logical_location, logical_path);
	//we dont need  '/' at the end of the full directory path)
	if (logical_location[strlen(logical_location)-1]=='/' && strcmp(logical_location,"/")!=0)
	{
		logical_location[strlen(logical_location)-1]=0x00;
	}

	//get filename
	dirnamePtr=strrchr(logical_location,'/');			//get last occurences of char '/'
	if (dirname!=NULL && dirnamePtr!=NULL)
		strcpy(dirname,dirnamePtr+1);					//get file name

	//get path (logical location)
	for (i=0; i<=(dirnamePtr-logical_location) ;i++ )
	{
		logical_location[i]=logical_path[i];		//move path to logical_location
	}
	logical_location[i]=0x00;
}

/*******************************************************************************************
* gfs_read_conf_file
* read configuration file
*
* @param : filename of configuration file
*
********************************************************************************************/
int gfs_read_conf_file(char *conf_file_name)
{
	verbose("gfs_read_conf_file() : file: %s\n",conf_file_name);
	FILE *fp;
	char data[PATH_LENGTH];
	char *tokenPtr;
	
	if ((fp=fopen(conf_file_name,"r"))==NULL)      //membuka file
	{
        verbose("gfs_read_conf_file() : Cannot open file %s\n", conf_file_name);
		return GFS_FAILED;
	}
     else
     {                       
         while(!feof(fp))                    //selama belum end of file..
         {
			 fgets(data,PATH_LENGTH, fp);
			 if (data[0]==';')
				continue;
			 tokenPtr=strtok(data,"=");
			 if (strcmp(tokenPtr, "mysql_server")==0)
			 {
				 tokenPtr=strtok(NULL,"\n");
				 strcpy(server,tokenPtr);
			 }
			 else if (strcmp(tokenPtr, "mysql_username")==0)
			 {
				 tokenPtr=strtok(NULL,"\n");
				 strcpy(user,tokenPtr);
			 }
			 else if (strcmp(tokenPtr, "mysql_password")==0)
			 {
				 tokenPtr=strtok(NULL,"\n");
				 strcpy(password,tokenPtr);
			 }
			 else if (strcmp(tokenPtr, "mysql_database")==0)
			 {
				 tokenPtr=strtok(NULL,"\n");
				 strcpy(database,tokenPtr);
			 }
			 else if (strcmp(tokenPtr, "mirror")==0)
			 {
				 tokenPtr=strtok(NULL,"\n");
				 if (tokenPtr!=NULL)
				 {
					MIRROR=atoi(tokenPtr);
				 }
				 else MIRROR=1;				 
			 }
			 else if (strcmp(tokenPtr, "chunks")==0)
			 {
				 tokenPtr=strtok(NULL,"\n");
				 if (tokenPtr!=NULL)
				 {
					CHUNKS=atoi(tokenPtr);
				 }
				 else CHUNKS=1;				 
			 }
		 }
		 fclose(fp);
		 if (CHUNKS==0)
		 	CHUNKS=1;
		 if (MIRROR==0)
			MIRROR=1;
		 
		 return GFS_SUCCESS;
	 }

	 

}

/*******************************************************************************************
* gfs_get_conf_file_section
* read a specific line in configuration file
* i.e. : section=value
*
* @param :	filename of configuration file
********************************************************************************************/
void gfs_get_conf_file_section(char *conf_file_name, char *section, char *value)
{
	verbose("gfs_get_conf_file_section() : file: %s\n",conf_file_name);
	FILE *fp;
	char data[PATH_LENGTH];
	char *tokenPtr;
	
	if ((fp=fopen(conf_file_name,"r"))==NULL)      //membuka file
        verbose("gfs_read_conf_file() : Failed opening file.\n");
     else
     {                       
         while(!feof(fp))                    //selama belum end of file..
         {
			 fgets(data,PATH_LENGTH, fp);
			 if (data[0]==';')
				continue;
			 tokenPtr=strtok(data,"=");
			 if (strcmp(tokenPtr, section)==0)
			 {
				tokenPtr=strtok(NULL,"\n");
				strcpy(value,tokenPtr);
				break;
			 }
		 }
		 fclose(fp);
	 }
	 
}

/*******************************************************************************************
* gfs_permission_bin2str
* convert 110100 to rw-r--
*
* @param : bin	: binary string
*		   str	: string
*
********************************************************************************************/
void gfs_permission_bin2str(char *bin, char *str)
{
	for (i=0; i<strlen(bin) ; i++ )
	{
		if (i==0 || i==3)
		{
			if (bin[i]=='1')
				str[i]='r';
			else
				str[i]='-';
		}
		else if (i==1 || i==4)
		{
			if (bin[i]=='1')
				str[i]='w';
			else
				str[i]='-';
		}
		else if (i==2 || i==5)
		{
			if (bin[i]=='1')
				str[i]='x';
			else
				str[i]='-';
		}
	}
	str[6]=0x00;
}

/*******************************************************************************************
* gfs_get_physical_filename
* convert "/dir1/dir2/filename.txt" to "dir1_dir2_filename.txt"
*
* @param : 
*
********************************************************************************************/
void gfs_get_physical_filename(char *filename, char *logical_location, char *physical_filename)
{
	for (i=1; i<(strlen(logical_location)+strlen(filename)) ;i++ )
	{
		if (logical_location[i]=='/')
			physical_filename[i-1]='_';
		else
			physical_filename[i-1]=logical_location[i];
	}
	strcat(physical_filename, filename);
	physical_filename[i-1]=0x00;
}

/*******************************************************************************************
* servername
* convert "ws-72.rsa.cs.ui.ac.id/gfs/dir1/file.txt" to "ws-72.rsa.cs.ui.ac.id"
*
* @param : 
*
*/
void gfs_get_servername(char *physical_path, char *servername)
{
	char *charPtr;
	int z;
	charPtr = strchr(physical_path, '/');
	if (charPtr!=NULL)
	{
		z=charPtr-physical_path;
		strncpy(servername,physical_path, z);
	}
	servername[z]=0x00;
}

/*******************************************************************************************
* gfs_get_servername_from_logical_location
*
*
* @param : 
* @return : 	-1 if failed
* 				file size if OK
*******************************************************************************************/
int gfs_get_servername_and_size_from_logical_location(char *filename, char *logical_location, char *servername)
{
	/*/get location
	char temp[PATH_LENGTH];
	if (!gfs_getLocation("rm", filename, logical_location, temp))		
		return -1;
	else
	{
		int size = atoi(row[7]); //row is queried in the previous getLocation()
		gfs_get_servername(temp, servername);
		return size;
	}*/

}

/*******************************************************************************************
* verbose
* if VERBOSE = 1 then we print all messages
*
* @param : string, format, and argument (printf like)
********************************************************************************************/
void verbose(char *fmt, ...)
{
	if (VERBOSE==1)
	{
		va_list ap; /* points to each unnamed arg in turn */
		char *p, *sval;
		int ival;
		double dval;
		long lval;
	
		va_start(ap, fmt); /* make ap point to 1st unnamed arg */
		for (p = fmt; *p; p++) {
	       if (*p != '%') {
	           putchar(*p);
	           continue;
	       }
	       switch (*++p) {
	       case 'd':
	           ival = va_arg(ap, int);
	           printf("%d", ival);
	           break;
	       case 'f':
	           dval = va_arg(ap, double);
	           printf("%f", dval);
	           break;
	       case 's':
	           for (sval = va_arg(ap, char *); *sval; sval++)
	               putchar(*sval);
	           break;
			  case 'l':
			   lval = va_arg(ap, long);
	           printf("%l", lval);
	           break;
	       default:
	           putchar(*p);
	           break;
	       }
	   }
	   va_end(ap); /* clean up when done */		
	}	
}


/*******************************************************************************************
* gfs_format_logical_location
* adds '/' at the end of string, but ignore if already exist
*
* @param : 
********************************************************************************************/
void gfs_format_logical_location(char *new_logical_location, char *logical_location)
{
	//check whether logical_location already follows proper naming of logical_location 
	//(needs '/' at the end of the full directory path)
	//verbose("%c",logical_path[strlen(logical_path)-1]);
	if (logical_location[strlen(logical_location)-1]!='/')
	{
		strcpy(new_logical_location, logical_location);
		strcat(new_logical_location,"/");		
	}
	else
		strcpy(new_logical_location, logical_location);
}

/*******************************************************************************************
* gfs_ping
* gfs_ping server_address
*
* @return : 0 if host is alive
* 			other than 0 is host is unreachable
* 			-1 is something is broken 
*******************************************************************************************/
int gfs_ping(char *server_address)
{
	char pingStr[128];
	sprintf(pingStr, "/bin/ping -q -c1 %s >/dev/null 2>&1", server_address);
	
	verbose("gfs_ping() : Trying to reach %s... \n", server_address);
	int result = system (pingStr);
    
    if (result == -1)
	{
        verbose("gfs_ping() : Cannot execute system(gfs_ping). Assuming host %s is alive.\n", server_address);
		result = 0;
	}
    else if (result != 0)
        verbose("gfs_ping() : Cannot reach host %s \n", server_address);
    else
        verbose("gfs_ping() : Host %s is alive\n", server_address);
    return result;
}

/*******************************************************************************************
* get_available_server
* gfs_ping server_address and get information from the database to the server list
*
* @return : number of host in the database
* 			0 
* 			-2 if memory error
********************************************************************************************/
int gfs_get_all_server(GFS_server *server)
{
	return gfs_ping_server(server,1);
}
int gfs_get_available_server(GFS_server *server)
{
	return gfs_ping_server(server,2);
}
int gfs_check_all_server(GFS_server *server)
{
	return gfs_ping_server(server,3);
}
int gfs_check_available_server(GFS_server *server)
{
	return gfs_ping_server(server,4);
}
int gfs_ping_server(GFS_server *server, int mode)
{
	int complete_all_thread=0;
	GFS_server temp=NULL;
	GFS_server tailPtr=NULL;
	pthread_t * threads=NULL;

	success_mirror=0;

	//mode 1	: get all server from database
	//mode 2	: get available server only, from database
	if (mode==1 || mode==2)
	{	
		//create query
		sprintf(query, "SELECT * FROM gfs_server");

		//open metadata database connection
		if (gfs_open_metadata())
			verbose("S:gfs_get_server() : connection success\n");
		else
		{
			verbose("S:gfs_get_server() : connection failed\n");
			return 0;
		}

		

		//verbose("\get_available_server() : query : %s", query);
		//run query
		if (mysql_query(conn, query))
		{
			fprintf(stderr, "%s\n", mysql_error(conn));
			return -1;
		}
		else 
		{
			verbose("S:gfs_get_server() : retrieving server list from DB\n");
			res = mysql_store_result(conn);		
			num_rows = mysql_num_rows(res);
			
			char tempStr[FILENAME_LENGTH];
			int pingResult;
			
			

			//create pthreads
			threads = (pthread_t*) realloc(threads, num_rows*sizeof(pthread_t));
			i=0;
			
			//get server list from database and check using pthread
			while ((row = mysql_fetch_row(res)) != NULL)
			{
				temp=malloc(sizeof(GFS_serverD));
				if (temp!=NULL)
				{
					//get hostname
					strcpy(temp->hostname,row[1]);
					strcpy(tempStr,row[1]);
					//get total and used space
					temp->total=atoll(row[2]);
					temp->used=atoll(row[3]);
					
					//gfs_ping and get status
					temp->online_status=0;
					temp->check_host_completed=0;

					//struct_temp=*temp;

					rc = pthread_create(&threads[i], NULL, gfs_check_port, (void *) temp);
					if (rc) 
					{
						printf("ERROR; return code from pthread_create() is %d\n", rc);
						return -1;
					}				
					
					temp->next=NULL;
					i++;
					
					if (*server==NULL)	
						*server=temp;
					else
						(tailPtr)->next=temp;
					tailPtr = temp; 
				}
				else 
				{
					verbose("S:gfs_get_server() : Out of Memory.\n");
					return -1;
				}	
			}
		}
	}
	//to check existing server list
	else if (mode==3 || mode==4)
	{
		j=0;
		temp=*server;
		while (temp!=NULL)
		{
			j++;
			temp=temp->next;
		}
		num_rows=j;
		//create pthreads
		threads = (pthread_t*) realloc(threads, j*sizeof(pthread_t));
		i=0;
		temp=*server;
		while (temp!=NULL)
		{
			//gfs_ping and get status
			temp->online_status=0;
			temp->check_host_completed=0;

			rc = pthread_create(&threads[i], NULL, gfs_check_port, (void *) temp);
			if (rc) 
			{
				printf("ERROR; return code from pthread_create() is %d\n", rc);
				return -1;
			}				
			
			temp=temp->next;
			i++;
		}
	}

	//check whether we have met enough mirror server. If not, just make do with what we have.
	for (;complete_all_thread==0 ; )
	{
		if (success_mirror>=MIRROR && mode!=3 && mode!=1)
		{
			for(i=0; i<num_rows; i++)
			{
				pthread_cancel(threads[i]);
			}
			break;
		}
		else 
		{
			i=0;
			temp=*server;
			while(temp!=NULL)
			{
				if (temp->check_host_completed==0)
				{
					break;
				}
				else if (i==num_rows-1)
				{
					complete_all_thread=1;
				}
				temp=temp->next;
				i++;
			}			
		}
	}

	
	//count final server, add status string and remove offline server
	i=0; //final server count
	GFS_server head;
	GFS_server prev=NULL;
	GFS_server toFree;
	head=*server;
	temp=*server;
	while (temp!=NULL)
	{
		if (temp->online_status==GFS_SUCCESS) 
			strcpy(temp->online,"online");
		else if (temp->online_status==-1)
			strcpy(temp->online, "offline");
		else
			 strcpy(temp->online, "unknown");

		if (mode==2 || mode==4)
		{
			if ( temp->online_status!=GFS_SUCCESS)
			{
				//remove offline server from the list
				if (prev!=NULL) //if not headPtr
				{
					prev->next=temp->next;
					//toFree=temp;
				}
				else //if headPtr
				{
					*server=(*server)->next;
					temp=temp->next;
					continue;
				}				
			}
			else 
			{
				i++;
				prev=temp;
			}
		}
		else
		{
			i++;
			prev=temp;		
		}
		temp=temp->next;
		//free(toFree);
	}
				
	return i;

}

/*******************************************************************************************
* gfs_update_mod_date
* updating the following item
*
* @return : 0
********************************************************************************************/
int gfs_update_mod_date(char *filename, char* logical_location)
{
	sprintf(query, "UPDATE metadata SET modified_date=CURRENT_TIMESTAMP WHERE filename='%s' AND logical_location='%s'"
		, filename, logical_location);
	
	verbose("gfs_update_mod_date() : query : %s\n", query); 
	//run query
	if (mysql_query(conn, query))
	{
		fprintf(stderr, "%s\n", mysql_error(conn));
		return 0;
	}
	else 
	{
		num_rows = mysql_affected_rows(conn);
		verbose("gfs_update_mod_date() : affected rows: %d\n", num_rows);

		return num_rows;					
	}		
}
int gfs_update_mod_date_abs_path(char *logical_path)
{
	char logical_location[strlen(logical_path)+1];
	//creates variable for putting separated directory location and name of the directory in question
	char dir_logical_location[PATH_LENGTH] = "" ;
	char dir_name[FILENAME_LENGTH] = "";
	
	//putting full logical path in the new created logical_location 
	strcpy(logical_location, logical_path);
	//separate dir name and location if logical_path is not root
	if (strcmp(logical_path, "/")!=0)
		gfs_get_location_and_dirname(logical_location, dir_logical_location, dir_name);
	else
		return 1;
		
	return gfs_update_mod_date(dir_name, dir_logical_location);
}

/*******************************************************************************************
* get_permission
* get_permission of file
*
* @return : 0 1
********************************************************************************************/
int gfs_get_permission(char *item_name, char *logical_location, char *mode)
{
	//base case: if root, then it is OK
	if (strcmp(logical_location, "/")==0 && strcmp(item_name,"")==0)
		return 1;

	if (GFS_SUPER_USER==GFS_SUCCESS)
	{
		return 1;
	}
	
	//open metadata database connection
	if (gfs_open_metadata())
		verbose("gfs_get_permission() : connection success\n");
	else
	{
		verbose("gfs_get_permission() : connection failed\n");
		return ECONN;
	}	
	
	char formatted_logical_location[strlen(logical_location)];
	gfs_format_logical_location(formatted_logical_location, logical_location);

	sprintf(query, "SELECT owner,permission FROM metadata WHERE filename='%s' AND logical_location='%s'", item_name, formatted_logical_location);
	
	verbose("get_permission() : query : %s\n", query); 
	//run query
	if (mysql_query(conn, query))
	{
		fprintf(stderr, "%s\n", mysql_error(conn));
		return -1;
	}
	else 
	{
		res = mysql_store_result(conn);	
		num_rows = mysql_num_rows(res);
		if (num_rows==0)
			return 0;
		
		row = mysql_fetch_row(res);
		verbose("get_permission() : affected rows: %d\n", num_rows);
		
		char local_user[FILENAME_LENGTH];
		char file_owner[strlen(row[0])];
		//get user id from system and get file owner from database		
		cuserid(local_user);
		strcpy(file_owner, row[0]);
		
		char permission[8];
		strcpy(permission, row[1]);
		
		verbose("get_permission() : local_user: %s, file_owner: %s\n", local_user, file_owner);
			
		//if user is the owner
		if (strcmp(local_user, file_owner)==0)
		{
			//check if user allowed to read
			if (strcmp(mode,"r")==0 && permission[0]=='1')
				return GFS_SUCCESS;
			//check if user allowed to write
			else if (strcmp(mode,"w")==0 && permission[1]=='1')
				return GFS_SUCCESS;
			//check if user allowed to execute
			else if (strcmp(mode,"x")==0 && permission[2]=='1') 
				return GFS_SUCCESS;
			//check if mode is owner
			else if (strcmp(mode,"owner")==0)
				return GFS_SUCCESS;
			else 
				return 0;
		}
		else		
		{
			//check if user allowed to read
			if (strcmp(mode,"r")==0 && permission[3]=='1')
				return GFS_SUCCESS;
			//check if user allowed to write
			else if (strcmp(mode,"w")==0 && permission[4]=='1')
				return GFS_SUCCESS;
			//check if user allowed to execute
			else if (strcmp(mode,"x")==0 && permission[5]=='1')
				return GFS_SUCCESS;
			else 
				return 0;
		}				
	}	
}
int gfs_get_permission_location(char *logical_path, char *mode)
{
	char logical_location[strlen(logical_path)+1];
	//creates variable for putting separated directory location and name of the directory in question
	char dir_logical_location[PATH_LENGTH] = "" ;
	char dir_name[FILENAME_LENGTH] = "";
	
	//putting full logical path in the new created logical_location 
	strcpy(logical_location, logical_path);
	//separate dir name and location if logical_path is not root
	if (strcmp(logical_path, "/")!=0)
		gfs_get_location_and_dirname(logical_location, dir_logical_location, dir_name);
	else
		return 1;
		
	return gfs_get_permission(dir_name, dir_logical_location, mode);
}
int gfs_rollback_metadata()
{
	if (mysql_rollback(conn)!=0)
	{
		verbose("gfs_rollback_metadata : failed rollback");
		return ERNS;
	}
}
/*******************************************************************************************
* gfs_check_port
* check port and host
*
* @return : 0 1
********************************************************************************************/
int isalive(struct sockaddr scanaddr)
{
    short int sock;          /* our main socket */
    long arg;                /* for non-block */
    fd_set wset;             /* file handle for bloc mode */
    struct timeval timeout;  /* timeout struct for connect() */

    sock = -1;

    sock = socket(AF_INET, SOCK_STREAM, 0);

    if( (arg = fcntl(sock, F_GETFL, NULL)) < 0) { 
        fprintf(stderr,
        "Error fcntl(..., F_GETFL) (%s)\n",
            strerror(errno));
        return 1;
    }

    arg |= O_NONBLOCK;
    if(fcntl(sock, F_SETFL, arg) < 0) {
        fprintf(stderr,
        "Error fcntl(..., F_SETFL)  (%s)\n",        strerror(errno));
        return 1;
    }

    /* 
     * set result stat then try a select if it can take
     * awhile. This is dirty but works 
     */
    int res = connect(sock,(struct sockaddr *)&scanaddr,
                      sizeof(scanaddr));

    if (res < 0) {
        if (errno == EINPROGRESS) {
            timeout.tv_sec = GFS_CHECK_HOST_TIMEOUT;
            timeout.tv_usec = 0;
            FD_ZERO(&wset);
            FD_SET(sock, &wset);
            int rc = select(sock + 1,NULL,
            &wset,NULL,&timeout);

            /* This works great on dead hosts */
            if (rc == 0 && errno != EINTR) {
                                verbose("Error connecting\n");
                                close (sock);
                return 1;
            }
        }
    }
    close(sock);
    return 0;
}

void *gfs_check_port(void *threadarg)
{
	struct GFS_serverD *my_data;
	my_data = (GFS_server) threadarg;
	char port[5];

	int sockfd;  
	struct addrinfo hints, *servinfo;
	int rv;
	char *addr=my_data->hostname;
	sprintf(port, "%s", GFS_PORT);
	
	
	//get address info
	memset(&hints, 0, sizeof hints);
	hints.ai_family = AF_UNSPEC; // use AF_INET6 to force IPv6
	hints.ai_socktype = SOCK_STREAM;

	if ((rv = getaddrinfo(addr, port, &hints, &servinfo)) != 0) {
	    //fprintf(stderr, "getaddrinfo: %s\n", gai_strerror(rv));
	}

	//create socket descriptor
	if ((sockfd = socket(servinfo->ai_family, servinfo->ai_socktype,servinfo->ai_protocol)) == -1) {
		//perror("socket");
		my_data->online_status=0;
		my_data->check_host_completed=1;
		return;
	}
	
	//check whether host is alive
	if (isalive(*servinfo->ai_addr)==0)
		verbose("S:gfs_ping_server() : Host %s is alive\n", addr);
	else {
		verbose("S:gfs_ping_server() : Host %s is not alive\n", addr);
		my_data->online_status=-1;
		my_data->check_host_completed=1;
		return;
	}
	
	/* So far so good - the host exists and is up; check the port and report */
        close (sockfd);
    	sockfd = socket(AF_INET, SOCK_STREAM, 0);
        if(connect(sockfd, servinfo->ai_addr, servinfo->ai_addrlen) == 0)
        {
	 		verbose("S:gfs_ping_server() : %s is open on %s\n", port, addr);
			pthread_mutex_lock( &mutex1 );
			success_mirror++;			
			pthread_mutex_unlock( &mutex1 );

			my_data->online_status=1;
			my_data->check_host_completed=1;
	 		return;
        }
        else
        {
			verbose("S:gfs_ping_server() : %s is not open on %s\n", port, addr);
			my_data->online_status=-1;
			my_data->check_host_completed=1;
			return;
        }

	if (servinfo == NULL) {
	    // looped off the end of the list with no connection
	    verbose("S:gfs_ping_server() : failed to connect\n");
		my_data->online_status=0;
		my_data->check_host_completed=1;
	    return;
	}

	freeaddrinfo(servinfo); // all done with this structure
} 

/*******************************************************************************************
* gfs_superuser
* check port and host
*
* @return : 0 1
********************************************************************************************/
int gfs_superuser_check(char *su_passwd)
{
	
	gfs_open_metadata();

	sprintf(query, "SELECT * FROM superuser WHERE password=MD5('%s')", su_passwd);

	//run query
	if (mysql_query(conn, query))
	{
		fprintf(stderr, "%s\n", mysql_error(conn));
		return -1;
	}
	else 
	{
		res = mysql_store_result(conn);	
		num_rows = mysql_affected_rows(conn);
		if (num_rows==1)
			return GFS_SUCCESS;
		else 
		{
			return 0;
		}
	}

	//gfs_close_metadata();
}

int gfs_superuser_chpwd(char *old_pwd, char *new_pwd)
{	
	gfs_open_metadata();

	sprintf(query, "UPDATE superuser SET password=MD5('%s') WHERE password=MD5('%s')", new_pwd, old_pwd);

	//run query
	if (mysql_query(conn, query))
	{
		fprintf(stderr, "%s\n", mysql_error(conn));
		return -1;
	}
	else 
	{
		num_rows = mysql_affected_rows(conn);
		if (num_rows==0)
			return GFS_FAILED;
		else
			return GFS_SUCCESS;
	}

	//gfs_close_metadata();
}

/*******************************************************************************************
* gfs_icmp_ping
* must be root
*
* @return : RTT in usec
********************************************************************************************/
uint16_t in_cksum(uint16_t *addr, unsigned len)
{
  uint16_t answer = 0;
  /*
   * Our algorithm is simple, using a 32 bit accumulator (sum), we add
   * sequential 16 bit words to it, and at the end, fold back all the
   * carry bits from the top 16 bits into the lower 16 bits.
   */
  uint32_t sum = 0;
  while (len > 1)  {
    sum += *addr++;
    len -= 2;
  }

  // mop up an odd byte, if necessary
  if (len == 1) {
    *(unsigned char *)&answer = *(unsigned char *)addr ;
    sum += answer;
  }

  // add back carry outs from top 16 bits to low 16 bits
  sum = (sum >> 16) + (sum & 0xffff); // add high 16 to low 16
  sum += (sum >> 16); // add carry
  answer = ~sum; // truncate to 16 bits
  return answer;
}

int gfs_icmp_ping(char *target)
{
	int s, i, cc, packlen, datalen = DEFDATALEN;
	struct hostent *hp;
	struct sockaddr_in to, from;
	//struct protoent	*proto;
	struct ip *ip;
	u_char *packet, outpack[MAXPACKET];
	char hnamebuf[MAXHOSTNAMELEN];
	char *hostname;
	struct icmp *icp;
	int ret, fromlen, hlen;
	fd_set rfds;
	struct timeval tv;
	int retval;
	struct timeval start, end;
	int /*start_t, */end_t;
	int cont = 1;

	to.sin_family = AF_INET;

	// try to convert as dotted decimal address, else if that fails assume it's a hostname
	to.sin_addr.s_addr = inet_addr(target);
	if (to.sin_addr.s_addr != (u_int)-1)
		hostname = target;
	else 
	{
		hp = gethostbyname(target);
		if (!hp)
		{
			//fprintf(stderr, "unknown host %s \n", target );
			return -1;
		}
		to.sin_family = hp->h_addrtype;
		bcopy(hp->h_addr, (caddr_t)&to.sin_addr, hp->h_length);
		strncpy(hnamebuf, hp->h_name, sizeof(hnamebuf) - 1);
		hostname = hnamebuf;
	}
	packlen = datalen + MAXIPLEN + MAXICMPLEN;
	if ( (packet = (u_char *)malloc((u_int)packlen)) == NULL)
	{
		//fprintf(stderr, "malloc error\n");
		return -1;
	}

/*
	if ( (proto = getprotobyname("icmp")) == NULL)
	{
		cerr << "unknown protocol icmp" << endl;
		return -1;
	}
*/
	if ( (s = socket(AF_INET, SOCK_RAW, IPPROTO_ICMP)) < 0)
	{
		//perror("socket");	/* probably not running as superuser */	
		return 0;
	}
	

	icp = (struct icmp *)outpack;
	icp->icmp_type = ICMP_ECHO;
	icp->icmp_code = 0;
	icp->icmp_cksum = 0;
	icp->icmp_seq = 12345;	/* seq and id must be reflected */
	icp->icmp_id = getpid();


	cc = datalen + ICMP_MINLEN;
	icp->icmp_cksum = in_cksum((unsigned short *)icp,cc);

	gettimeofday(&start, NULL);

	i = sendto(s, (char *)outpack, cc, 0, (struct sockaddr*)&to, (socklen_t)sizeof(struct sockaddr_in));
	if (i < 0 || i != cc)
	{
		if (i < 0)
			//perror("sendto error");
			;
		//printf( "wrote %s %d chars, ret= %d", hostname, cc, i);
	}
	
	// Watch stdin (fd 0) to see when it has input.
	FD_ZERO(&rfds);
	FD_SET(s, &rfds);
	// Wait up to one seconds.
	tv.tv_sec = 1;
	tv.tv_usec = 0;

	while(cont==1)
	{
		retval = select(s+1, &rfds, NULL, NULL, &tv);
		if (retval == -1)
		{
			//perror("select()");
			return -1;
		}
		else if (retval)
		{
			fromlen = sizeof(struct sockaddr_in);
			if ( (ret = recvfrom(s, (char *)packet, packlen, 0,(struct sockaddr *)&from, (socklen_t*)&fromlen)) < 0)
			{
				//perror("recvfrom error");
				return -1;
			}

			// Check the IP header
			ip = (struct ip *)((char*)packet); 
			hlen = sizeof( struct ip ); 
			if (ret < (hlen + ICMP_MINLEN)) 
			{ 
				//fprintf(stderr, "packet too short (%d bytes) from %s \n" ,ret, hostname);
				return -1; 
			} 

			// Now the ICMP part 
			icp = (struct icmp *)(packet + hlen); 
			if (icp->icmp_type == ICMP_ECHOREPLY)
			{
				//printf("Recv: echo reply\n");
				if (icp->icmp_seq != 12345)
				{
					//printf("received sequence # %d \n" ,icp->icmp_seq );
					continue;
				}
				if (icp->icmp_id != getpid())
				{
					//printf("received id %d\n" ,icp->icmp_id );
					continue;
				}
				cont = 0;
			}
			else
			{
				//printf("Recv: not an echo reply\n");
				continue;
			}
	
			gettimeofday(&end, NULL);
			end_t = 1000000*(end.tv_sec - start.tv_sec) + (end.tv_usec - start.tv_usec);

			if(end_t < 1)
				end_t = 1;

			//printf("Elapsed time = %d usec\n" ,end_t);
			return end_t;
		}
		else
		{
			//printf( "No data within one seconds.\n");
			return 0;
		}
	}
	return 0;
}

